#!/usr/bin/python
# Copyright 2007 Alex K (wtwf.com) All rights reserved.
# $Id: shutupjaiku.py,v 2.3 2008-05-26 00:39:36 ark Exp $

"""shutupjaiku a jabber robot that posts to jaiku and won't answer you back.

-h, --help
  display this help message
-d
  debug output
-u user [--user=user]
  connect as this jabber/gTalk user
-p password [--password=password]
  use this password for the jabber_user
-c config_filename --config=config_filename
  load this config file

config files can define the following global variables

global user, password
user=
password=

People connect to your robot and sign in just like the jaiku@jaiku.com robot
"sign in <username> <password>"
The password it not stored in the server, although the password is used to get
the api key and the api key IS stored.

TODO(ark) make it work standalone for one jabber user and one jaiku user
    (no need to sign in)
TODO(ark) provide an admin user and have specal status commands for that user.
"""

import getopt
import getpass
import httplib
import logging
import os
import re
import sys
import time
import urllib
import urllib2
import xmlrpclib

import PyGtalkRobot

if sys.version_info < (2, 4):
    raise "You probably need python 2.5 or greater"

_MAX_USERS = 1024

user = None
password = None

def usage(code, msg=''):
  if code:
    fd = sys.stderr
  else:
    fd = sys.stdout
  PROGRAM = os.path.basename(sys.argv[0])
  print >> fd, __doc__ % locals()
  if msg:
    print >> fd, msg
  sys.exit(code)


class ShutupJaikuBot(PyGtalkRobot.GtalkRobot):

  #This method is used to send email for users.
  def command_002_SignIn(self, user, message, args):
    '''sign\s+in\s+(\S+)\s+(.*)(?i)'''
    jaiku_user = args[0]
    jaiku_password = args[1]

    if len(self.jaiku_users) > _MAX_USERS:
      self.replyMessage(user, "Too many users")
      return

    try:
      jaiku_personal_key = GetJaikuPersonalKey(jaiku_user, jaiku_password)
    except ApiKeyFetchError:
      self.replyMessage(user,
                        "Failed to get authentication key. Wrong Password?")
      return

    self.jaiku_users[user.getStripped()] = JaikuUser(jaiku_user,
                                                     jaiku_personal_key)
    self.replyMessage(user, "user '%s' signed in." % jaiku_user)


  def command_100_default(self, user, message, args):
    '''.*?(?s)(?m)'''

    try:
        jaiku_user = self.jaiku_users[user.getStripped()]
    except:
        self.replyMessage(user,
                          "You must log in first! Send:\n"
                          "sign in <username> <password>")
        return
    try:
        print message
        self.jaiku.presence.send({"user": jaiku_user.user,
                                  "personal_key": jaiku_user.personal_key,
                                  "message": message})
    except:
        print "FAILED!"
        self.replyMessage(user, "Jaiku API Epic Fail!")


  def state_100_default(self, user, message, args):
    '''[^-].*'''
    self.command_100_default(user, message, args)

class JaikuUser:
  """Stores information about a Jaiku User.

  At some point we might store a last used timestamp so we can expire people.
  """

  def __init__(self, user, personal_key):
    self.user = user
    self.personal_key = personal_key


class ApiKeyFetchError(Exception):
  pass


def GetJaikuPersonalKey(user, password):
  """Finds the Jaiku API personal_key from a username and password.

  raises:
    ApiKeyFetchError on failure.

  One day I'll learn to use urllib2 properly and cookie parsing and stuff.
  """

  # login and find a cookie
  login_url = "http://jaiku.com/login"
  request_body = urllib.urlencode({'log': user,
                                   'pwd': password,
                                   'rememberme': '1'})
  # Open a connection to the authentication server.
  auth_connection = httplib.HTTPConnection('jaiku.com')
  # Begin the POST request to the client login service.
  auth_connection.putrequest('POST', '/login')
  # Set the required headers for an Account Authentication request.
  auth_connection.putheader('Content-type',
                            'application/x-www-form-urlencoded')
  auth_connection.putheader('Content-Length', str(len(request_body)))
  auth_connection.endheaders()
  auth_connection.send(request_body)
  auth_response = auth_connection.getresponse()
  if auth_response.status == 303:
    cookie_str = auth_response.getheader("set-cookie")
    # TODO(ark) parse this properly!
    res = re.search("(jaikuuser_[^;]*).*(jaikupass_[^;]*)", cookie_str)
    if res:
      auth_cookie = "%s; %s" % (res.group(1), res.group(2))
      apikey_url = "http://api.jaiku.com/key"
      req = urllib2.Request(url=apikey_url)
      req.add_header('Cookie', auth_cookie)
      f = urllib2.urlopen(req)
      return f.read()
  raise ApiKeyFetchError

def Main():
  logging.basicConfig()
  logging.getLogger().setLevel(logging.INFO)

  bot = ShutupJaikuBot()
  # This is where we store credentials for all users using the system
  # key is the IM username, value is a JaikuUser object
  bot.jaiku_users = {}
  bot.jaiku = xmlrpclib.ServerProxy("http://api.jaiku.com/xmlrpc")
  # Clear out the status (empty string seems to make it PyGtalkRobot)
  bot.setState('available', " ")

  try:
    opts, args = getopt.getopt(sys.argv[1:], 'hdu:p:c:',
                               ['help', 'debug', 'user=', 'password=',
                                'config='])
  except getopt.error, msg:
    usage(1, msg)

  if args:
    usage(1)

  config_file = None
  global user, password, jaiku_users
  for opt, arg in opts:
    if opt in ('-h', '--help'):
      usage(0)
    if opt in ('-d', '--debug'):
      logging.info("Setting logging level to debug.")
      logging.getLogger().setLevel(logging.DEBUG)
      logging.debug("logging level set to debug.")
    if opt in ('-u', '--user'):
      user = arg
    if opt in ('-c', '--config'):
      config_file = arg


  # possibly load something extra...
  if config_file is None:
    config_file = os.path.expanduser("~/.shutupjaikurc.py")
  if os.path.exists(config_file):
    logging.debug("Loading: %s", config_file)
    execfile(config_file)
  if not user:
    Usage(2, "You must provide a username")
  if not password:
    password = getpass.getpass("Enter Password for '%s': " % user)
  if jaiku_users:
    bot.jaiku_users = jaiku_users
  bot.start(user, password)



if __name__ == "__main__":
  Main()
